#ifndef __HLOG_INTERNAL_H__
#define __HLOG_INTERNAL_H__

#define likely(x)	(__builtin_expect(!!(x), 1))
#define unlikely(x)	(__builtin_expect(!!(x), 0))

#define hlog_rmb()     __asm volatile("lfence" ::: "memory")
#define hlog_wmb()     __asm volatile("sfence" ::: "memory")
#define hlog_mb()      __asm volatile("mfence" ::: "memory")

#define barrier() __asm__ __volatile__("": : :"memory")

#define LOAD(x)					\
	({					\
		int ___v;			\
		___v = *(volatile int *)(&x);	\
		hlog_mb();			\
		___v;				\
	})

#define STORE(x, v)				\
	({					\
		hlog_mb();			\
		*(volatile int *)(&x) = v;	\
	})

#define _pl()	fprintf(stderr, "line=%d, tid=%d\n", __LINE__, gettid())

#define HLOG_MAX_THR		128		//支持的最大业务线程数量
#define HLOG_RING_SIZE		(g_ctrl->page_size)
#define HLOG_THR_BUF_LEN	(HLOG_RING_SIZE - 32)
#define HLOG_ENTRY_ALIGN	32		//环形缓冲区中entry对齐单位

#define CTIME_BUF_LEN		32
#define HLOG_PATH_MAX		256		//文件名的最大尺寸

#define ENTRY_NULL		0
#define ENTRY_STRING		1

struct hlog_entry_hdr {
	char	type;
	char	prio;
	char	rsv[2];
	int	cap;		// cap是包含hlog_entry头和后面字符串缓冲区，以及
				// 对齐填充部分的总长度
	int	len;		// len是字符串内容的长度

	struct timespec ts;	// 这条日志的时间戳
};

struct hlog_ring {
	int	head;		// 向缓冲区中添加数据的指针
	int	tail;		// 冲缓冲区中取出数据的指针
	char	rsv[24];
	char	buf[];
};

struct hlog_thr {
	int	th_idx;		// 这个线程在hlog_ctrl->thr[]中的位置
	int	lt_idx;		// 这个线程在hlog_ctrl->lt_map[]中的位置
	pid_t	tid;
	int	blocked;			//有thr因为ring满而被阻塞
	long	nr_blocked;			//这是一个统计值，blocked累加值
	pthread_mutex_t mutex;
	pthread_cond_t thr_cond;		//用来通知业务线程
	struct hlog_ring *ring;
	pthread_key_t key;
};

enum logger_status {
	LS_POLL = 0,
	LS_WAIT = 1
};

/* hlog_ctrl是hlog的总控结构，是hlog用到的所有其他数据结构的根 */
struct hlog_ctrl {
	pthread_t logger_thr;

	char log_file[HLOG_PATH_MAX];		//日志文件的文件名
	int persist_prio;			//大于等于本级别，立即持久化
	int log_all_prio;			//大于等于本级别，全部输出
	int log_fd;				//日志文件的描述符
	int page_size;

	//性能统计
	long nr_wait;
	long nr_refresh;
	long nr_wakeup;
	long nr_logs;

	int exit;				//修改需要持有mutex
	enum logger_status status;		//修改需要持有mutex

	pthread_mutex_t mutex;
	pthread_cond_t logger_cond;		//用来通知logger线程

	struct timespec last_refresh;
	struct loser_tree *lt;

	int thr_err;
	int nr_thr;				//当前线程数量
	struct hlog_thr *thr[HLOG_MAX_THR];

	// 线程在thr[]数组中排列允许用空洞，thr中间允许有NULL，包含空洞thr数组
	// 的总有效长度就比较长，对败者树算法不利。因此，定义了一个败者树算法
	// 使用的紧密排列的线程索引数组，这个lt_map中间没有空洞，有效长度比较
	// 短。从败者树索引到业务线程指针，经过两级映射, 如下所示：
	//    thr_idx = lt_map[lt_idx];
	//    thr_p = g_ctrl->[thr_idx];
	int lt_map[HLOG_MAX_THR];

	char mmap_file[HLOG_PATH_MAX];		//共享内存区的文件名
	size_t shared_mem_len;			//共享内存区的大小
	char *shared_mem;			//共享内存区
	int mmap_fd;				//mmap文件的描述
};

#endif //__HLOG_INTERNAL_H__


